package cn.tedu.mall.seckill.init;

import cn.tedu.mall.pojo.seckill.vo.SeckillSkuVO;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuVO;
import cn.tedu.mall.seckill.mappers.SeckillSkuMapper;
import cn.tedu.mall.seckill.mappers.SeckillSpuMapper;
import cn.tedu.mall.seckill.service.Impl.SeckillSkuServiceImpl;
import cn.tedu.mall.seckill.service.Impl.SeckillSpuServiceImpl;
import cn.tedu.mall.seckill.utils.BloomFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangyiping
 * @date 2024/1/19
 */
@Component
@Slf4j
public class SeckillInitializer {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private SeckillSpuMapper seckillSpuMapper;
    @Autowired
    private SeckillSkuMapper seckillSkuMapper;
    @Autowired
    private BloomFilter bloomFilter;
    // 初始化数据:每一种数据单独为一个方法
    public void initStart() {
        // 初始化开始
        initSpus();
    }

    private void initSpus() {
        // 从数据库查询所有秒杀的spu
        // select * from seckill_spu
        List<SeckillSpuVO> seckillSpuVOList = seckillSpuMapper.selectAll();
        if (seckillSpuVOList.isEmpty()) {
            log.error("当前数据库没有spu预热的数据，请检查数据库！");
            return;
        }
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 将查询到到数据，封装成List<SeckillSpuVO>放入redis，并设置超时时长
        valueOperations.set(SeckillSpuServiceImpl.SECKILL_SPUS_KEY, seckillSpuVOList, 1, TimeUnit.DAYS);
        // 循环将单个SeckillSpuVO数据存入redis，并设置超时时长
        for (SeckillSpuVO seckillSpuVO : seckillSpuVOList) {
            Long spuId = seckillSpuVO.getId();
//            // 将每个spuId放入布隆过滤器，初始化过滤器
//            bloomFilter.spuAdd(spuId);
            // 生成每个spu秒杀下单提交路径
            String url = "/seckill/" + (new Random().nextInt(9000) + 1000);
            seckillSpuVO.setUrl(url);
            valueOperations.set(SeckillSpuServiceImpl.SECKILL_SPU_PREFIX + spuId, seckillSpuVO, 1, TimeUnit.DAYS);
            // 初始化 sku
            initSkus(spuId);
        }
    }

    private void initSkus(Long spuId) {
        // select * from seckill_sku where spu_id = #{spuId}
        List<SeckillSkuVO> seckillSkuVOList = seckillSkuMapper.selectSkuBySpuId(spuId);
        if (seckillSkuVOList == null || seckillSkuVOList.isEmpty()) {
            log.error("当前数据库没有sku预热的数据，请检查数据库！");
            return;
        }
        String skusKey = SeckillSkuServiceImpl.SECKILL_SKUS_PREFIX + spuId;
        redisTemplate.opsForValue().set(skusKey, seckillSkuVOList, 1, TimeUnit.DAYS);
        // 将每个sku库存数预热
        for (SeckillSkuVO seckillSkuVO : seckillSkuVOList) {
            Long skuId = seckillSkuVO.getId();
            Integer stock = seckillSkuVO.getStock();
            stringRedisTemplate.opsForValue().set(SeckillSkuServiceImpl.SECKILL_SKU_STOCK_PREFIX + skuId, stock + "");
        }
    }

    /**
     * 定时同步库存，每5秒钟执行一次
     * 作用：秒杀下单后，有的用户没有支付订单，超过支付时间后需要还库存，可供其他用户秒杀
     */
    private boolean startTimer = true;
    public void syncStock() {
        while (startTimer) {
            try {
                // 读取sku数据库秒杀stock存放到set，重置到redis对应库存数据中
                List<SeckillSkuVO> seckillSkuVOList = seckillSkuMapper.selectAll();
                for (SeckillSkuVO seckillSkuVO : seckillSkuVOList) {
                    Long skuId = seckillSkuVO.getId();
                    Integer stock = seckillSkuVO.getStock();
                    stringRedisTemplate.opsForValue().set(SeckillSkuServiceImpl.SECKILL_SKU_STOCK_PREFIX + skuId, stock + "");
                }
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
